package com.zkt.subject.domain.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ObjUtil;
import com.alibaba.fastjson2.JSON;
import com.zkt.subject.common.constants.SubjectConstant;
import com.zkt.subject.common.entity.PageResult;
import com.zkt.subject.domain.convert.SubjectInfoConverter;
import com.zkt.subject.domain.entity.SubjectUserScore;
import com.zkt.subject.infra.entity.SubjectInfoBO;
import com.zkt.subject.domain.entity.SubjectOptionBO;
import com.zkt.subject.domain.handler.subject.SubjectTypeHandler;
import com.zkt.subject.domain.handler.subject.SubjectTypeHandlerFactory;
import com.zkt.subject.domain.service.SubjectInfoDomainService;
import com.zkt.subject.infra.basic.entity.SubjectInfo;
import com.zkt.subject.infra.basic.entity.SubjectLabel;
import com.zkt.subject.infra.basic.entity.SubjectMapping;
import com.zkt.subject.infra.basic.entity.SubjectUserRecord;
import com.zkt.subject.infra.basic.service.ISubjectInfoService;
import com.zkt.subject.infra.basic.service.ISubjectLabelService;
import com.zkt.subject.infra.basic.service.ISubjectMappingService;
import com.zkt.subject.infra.basic.service.ISubjectUserRecordService;
import com.zkt.subject.infra.rpc.UserRPC;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author 赵开泰
 * @program jc-club
 * @date 2024/9/30
 * @description 题目标签领域服务
 **/

@Service
@Slf4j
public class SubjectInfoDomainServiceImpl implements SubjectInfoDomainService {
	
	@Resource
	private ISubjectInfoService subjectInfoService;
	
	@Resource
	private ISubjectMappingService subjectMappingService;
	
	@Resource
	private ISubjectLabelService subjectLabelService;
	
	@Resource
	private SubjectTypeHandlerFactory subjectTypeHandlerFactory;
	
	@Resource
	private ISubjectUserRecordService subjectUserRecordService;
	
	@Resource
	private UserRPC userRPC;
	
	@Resource
	private RocketMQTemplate rocketMQTemplate;
	
	/**
	 * 新增题目
	 *
	 * @param subjectInfoBO 新增的题目数据
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public void add(SubjectInfoBO subjectInfoBO) {
		if (log.isInfoEnabled()) {
			log.info("SubjectInfoDomainServiceImpl.add.bo:{}", JSON.toJSONString(subjectInfoBO));
		}
		
		SubjectInfo subjectInfo = SubjectInfoConverter.INSTANCE.convertBoToInfo(subjectInfoBO);
		subjectInfoService.save(subjectInfo);
		
		SubjectTypeHandler handler = subjectTypeHandlerFactory.getHandler(subjectInfo.getSubjectType());
		subjectInfoBO.setId(subjectInfo.getId());
		handler.add(subjectInfoBO);
		List<Long> labelIds = subjectInfoBO.getLabelIds();
		List<SubjectMapping> mappingList = new ArrayList<>();
		labelIds.forEach(labelId -> {
			SubjectLabel labelInfo = subjectLabelService.getById(labelId);
			
			SubjectMapping subjectMapping = new SubjectMapping();
			subjectMapping.setSubjectId(subjectInfo.getId());
			subjectMapping.setCategoryId(labelInfo.getCategoryId());
			subjectMapping.setLabelId(labelId);
			mappingList.add(subjectMapping);
		});
		subjectMappingService.saveBatch(mappingList);
	}
	
	/**
	 * 修改题目
	 *
	 * @param subjectInfoBO 修改的题目数据
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public void update(SubjectInfoBO subjectInfoBO) {
		SubjectInfo subjectInfo = SubjectInfoConverter.INSTANCE.convertBoToInfo(subjectInfoBO);
		subjectInfoService.updateById(subjectInfo);
		
		SubjectTypeHandler handler = subjectTypeHandlerFactory.getHandler(subjectInfo.getSubjectType());
		subjectInfoBO.setId(subjectInfo.getId());
		handler.update(subjectInfoBO);
		
		subjectMappingService.lambdaUpdate()
				.eq(SubjectMapping::getSubjectId, subjectInfo.getId())
				.remove();
		
		List<Long> labelIds = subjectInfoBO.getLabelIds();
		List<SubjectMapping> mappingList = new ArrayList<>();
		labelIds.forEach(labelId -> {
			SubjectLabel labelInfo = subjectLabelService.getById(labelId);
			
			SubjectMapping subjectMapping = new SubjectMapping();
			subjectMapping.setSubjectId(subjectInfo.getId());
			subjectMapping.setCategoryId(labelInfo.getCategoryId());
			subjectMapping.setLabelId(labelId);
			mappingList.add(subjectMapping);
		});
		subjectMappingService.saveBatch(mappingList);
	}
	
	/**
	 * 删除题目
	 *
	 * @param subjectInfoBO 请求参数
	 */
	@Override
	@Transactional(rollbackFor = Exception.class)
	public void delete(SubjectInfoBO subjectInfoBO) {
		SubjectInfo subjectInfo = SubjectInfoConverter.INSTANCE.convertBoToInfo(subjectInfoBO);
		SubjectInfo subject = subjectInfoService.getById(subjectInfo.getId());
		
		subjectInfoService.removeById(subject.getId());
		SubjectTypeHandler handler = subjectTypeHandlerFactory.getHandler(subject.getSubjectType());
		handler.delete(subject.getId());
		subjectMappingService.lambdaUpdate()
				.eq(SubjectMapping::getSubjectId, subject.getId())
				.remove();
	}
	
	/**
	 * 分页查询题目列表
	 *
	 * @param subjectInfoBO 查询条件
	 * @return 查询结果
	 */
	@Override
	public PageResult<SubjectInfoBO> getSubjectPage(SubjectInfoBO subjectInfoBO) {
		PageResult<SubjectInfoBO> pageResult = new PageResult<>();
		pageResult.setPageNo(subjectInfoBO.getPageNo());
		pageResult.setPageSize(subjectInfoBO.getPageSize());
		
		int start = (subjectInfoBO.getPageNo() - 1) * subjectInfoBO.getPageSize();
		SubjectInfo subjectInfo = SubjectInfoConverter.INSTANCE.convertBoToInfo(subjectInfoBO);
		long count = subjectInfoService.countByCondition(subjectInfo, subjectInfoBO.getLabelId(), subjectInfoBO.getCategoryId());
		
		if (count == 0) {
			return pageResult;
		}
		
		List<SubjectInfo> subjectInfoList = subjectInfoService.queryPage(subjectInfo, subjectInfoBO.getLabelId(),
				subjectInfoBO.getCategoryId(), start, subjectInfoBO.getPageSize());
		List<SubjectInfoBO> subjectInfoBOList = SubjectInfoConverter.INSTANCE.convertListInfoToBo(subjectInfoList);
		
		subjectInfoBOList.forEach(subject -> {
			List<Long> LabelList = subjectMappingService.lambdaQuery()
					.eq(SubjectMapping::getSubjectId, subject.getId())
					.list()
					.stream()
					.map(SubjectMapping::getLabelId)
					.toList();
			List<String> labelNameList = subjectLabelService.listByIds(LabelList).stream()
					.map(SubjectLabel::getLabelName)
					.toList();
			subject.setLabelNames(labelNameList);
		});
		
		pageResult.setResult(subjectInfoBOList);
		pageResult.setTotal(count);
		return pageResult;
	}
	
	/**
	 * 查询题目详情
	 *
	 * @param subjectInfoBO 查询参数
	 * @return 题目详情
	 */
	@Override
	public SubjectInfoBO querySubjectInfo(SubjectInfoBO subjectInfoBO) {
		SubjectInfo subjectInfo = subjectInfoService.getById(subjectInfoBO.getId());
		
		SubjectTypeHandler handler = subjectTypeHandlerFactory.getHandler(subjectInfo.getSubjectType());
		SubjectOptionBO subjectOptionBO = handler.queryBySubjectId(subjectInfo.getId());
		
		SubjectInfoBO bo = SubjectInfoConverter.INSTANCE.convertOptionAndInfoToBo(subjectOptionBO, subjectInfo);
		
		List<SubjectMapping> subjectMappingList = subjectMappingService.lambdaQuery().eq(SubjectMapping::getSubjectId,
				subjectInfo.getId()).list();
		List<Long> labelIdList = subjectMappingList.stream().map(SubjectMapping::getLabelId).toList();
		
		List<SubjectLabel> subjectLabelList = subjectLabelService.listByIds(labelIdList);
		List<String> labelNameList = subjectLabelList.stream().map(SubjectLabel::getLabelName).collect(Collectors.toList());
		
		bo.setLabelIds(labelIdList);
		bo.setLabelNames(labelNameList);
		return bo;
	}
	
	/**
	 * 根据id批量查询题目详情
	 *
	 * @param subjectIds 题目id
	 * @return 题目详情
	 */
	@Override
	public List<SubjectInfoBO> querySubjectInfoByIds(List<Long> subjectIds) {
		if (CollUtil.isEmpty(subjectIds)) {
			return ListUtil.empty();
		}
		
		return subjectInfoService.querySubjectInfoByIds(subjectIds);
	}
	
	/**
	 * 根据标签id批量查询题目详情
	 *
	 * @param labelIds 标签id
	 * @return 题目详情
	 */
	@Override
	public List<SubjectInfoBO> querySubjectInfoByLabelIds(List<Long> labelIds) {
		List<Long> subjectIdList = subjectMappingService.lambdaQuery()
				.in(SubjectMapping::getLabelId, labelIds)
				.list()
				.stream()
				.map(SubjectMapping::getSubjectId)
				.toList();
		
		return this.querySubjectInfoByIds(subjectIdList);
	}
	
	/**
	 * 提交题目
	 *
	 * @param subjectInfoBO 提交信息
	 * @return 得分
	 */
	@Override
	public SubjectUserScore submit(SubjectInfoBO subjectInfoBO) {
		if (ObjUtil.isNull(subjectInfoBO.getSubjectType())) {
			subjectInfoBO.setSubjectType(subjectInfoService.getById(subjectInfoBO.getId()).getSubjectType());
		}
		
		SubjectTypeHandler handler = subjectTypeHandlerFactory.getHandler(subjectInfoBO.getSubjectType());
		
		SubjectInfoBO subjectInfo = this.querySubjectInfo(subjectInfoBO);
		subjectInfoBO.setSubjectScore(subjectInfo.getSubjectScore());
		SubjectUserScore userScore = handler.getUserScore(subjectInfoBO);
		
		List<SubjectUserRecord> subjectUserRecordList = new LinkedList<>();
		subjectInfo.getLabelIds().forEach(labelId -> {
			SubjectUserRecord subjectUserRecord = new SubjectUserRecord();
			
			subjectUserRecord.setUserId(userRPC.getUserInfo(null).getId());
			subjectUserRecord.setSubjectId(subjectInfo.getId());
			subjectUserRecord.setSubjectName(subjectInfo.getSubjectName());
			subjectUserRecord.setSubjectDifficult(subjectInfo.getSubjectDifficult());
			subjectUserRecord.setSubjectScore(subjectInfo.getSubjectScore());
			subjectUserRecord.setUserScore(userScore.getUserScore());
			subjectUserRecord.setUseTime(subjectInfoBO.getUseTime());
			subjectUserRecord.setUserAnswer(userScore.getUserAnswer());
			subjectUserRecord.setLabelId(labelId);
			
			subjectUserRecordList.add(subjectUserRecord);
		});
		
		subjectUserRecordService.saveBatch(subjectUserRecordList);
		
		for (SubjectUserRecord subjectUserRecord : subjectUserRecordList) {
			// rocketMQTemplate.convertAndSend(SubjectConstant.DEAL_NEW_QUESTION, subjectUserRecord);
			SendResult sendResult = rocketMQTemplate.syncSend(SubjectConstant.DEAL_NEW_QUESTION, subjectUserRecord);
			System.out.println(sendResult);
		}
		
		return userScore;
	}
}
